﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace CloudShot.Utils.Encoding
{
  public class EncodingWorker
  {
    private readonly object _queueLock = new object();
    private readonly object _recordingLock = new object();

    private readonly Queue<Bitmap> _drawingQueue = new Queue<Bitmap>();
    private bool _isFinishedRecording = false;
    private bool _isCanceled = false;
    private readonly BackgroundWorker _worker = new BackgroundWorker();
    private readonly AnimatedGifEncoder _encoder;
    

    public List<Bitmap> Bitmaps = new List<Bitmap>();
    public event Action FinishEncoding;
    public event Action<int> ProgressChanged;

    public int AddedImageCount { get; private set; }
    public Image Image { get; private set; }

    public EncodingWorker(int fps, int width, int height)
    {
      _worker.WorkerReportsProgress = true;
      _worker.WorkerSupportsCancellation = true;

      _worker.DoWork += OnGifCreating;
      _worker.ProgressChanged += OnGifCreatingProgressChanged;
      _worker.RunWorkerCompleted += OnGifCreated;


      _encoder = new AnimatedGifEncoder(width, height, fps);
      _encoder.SetRepeat(0); //if checked, 0 else -1

      _encoder.Start();

      _worker.RunWorkerAsync();
    }

    public void FinisheRecording(bool cancelRecording)
    {
      //TODO do we need this?
      lock (_recordingLock)
      {
        _isCanceled = cancelRecording;
        _isFinishedRecording = true;  
      }
    }

    public void Cancel()
    {
      if (_worker.IsBusy)
        _worker.CancelAsync();
    }

    public void AddImage(Bitmap image)
    {
      //Bitmaps.Add(image);

      lock (_queueLock)
      {
        _drawingQueue.Enqueue(image);
        AddedImageCount++;
      }
    }


    private void OnGifCreating(object sender, DoWorkEventArgs e)
    {
      bool workInProgress = true;

      do
      {
        if (_isCanceled)
        {
          e.Cancel = true;
          return;
        }

        Bitmap img = null;

        int imageLeftCount = 0;
        lock (_drawingQueue)
        {
          imageLeftCount = _drawingQueue.Count;
          if (imageLeftCount > 0)
            img = _drawingQueue.Dequeue();
        }

        if (img != null)
        {
          _worker.ReportProgress(AddedImageCount - imageLeftCount);
          _encoder.AddFrame(img);
        }
        else
        {
          lock (_recordingLock)
          {
            workInProgress = !_isFinishedRecording;
          }

          //TODO refactor this
          Thread.Sleep(1);
        }

      } while (workInProgress);


      e.Result = true;
    }

    private void OnGifCreatingProgressChanged(object sender, ProgressChangedEventArgs e)
    {
      InvokeProgressChanged(e.ProgressPercentage);
    }

    private void OnGifCreated(object sender, RunWorkerCompletedEventArgs e)
    {
      if (!e.Cancelled)
        Image = _encoder.Finish();

      InvokeFinishEncoding();
    }

    protected virtual void InvokeProgressChanged(int obj)
    {
      Action<int> handler = ProgressChanged;
      if (handler != null)
        handler(obj);
    }

    protected virtual void InvokeFinishEncoding()
    {
      Action handler = FinishEncoding;
      if (handler != null)
        handler();
    }
  }
}
